﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using DARE.Properties;
//using DebuggerTools;
//using DebuggerTools.Tools;

namespace DARE
{
    /// <summary>
    /// Class use to load and unload ressources
    /// This class will register all ressources used into the current Scene.
    /// (to this used Load and UnLoad method)
    /// To pre-load the new asset and unload old asset use the static method Load
    /// <example>
    /// <code>
    /// // Like the ContentManager to load asset use this code
    /// CResourceMgr.Load<Texture2D>("Head.tga");
    /// </code>
    /// </example>
    /// </summary>
    public static class CResourceMgr
    {
        #region Field

        private static ContentManager m_ctmanager;
        private static CScene m_currentScene;
        private static MethodInfo m_contentReader = null;
        private static MethodInfo m_readAsset = null;
        private static MethodInfo m_cleanPath = null;
        private static Dictionary<string, object> m_dicoInfo;
        private static uint m_effectId = 0;

        #endregion

        #region CTOR

        static CResourceMgr()
        {
            //m_ctmanager = new ContentManager(CDare.Instance.Game.Services);
            //m_ctmanager.RootDirectory = CDare.Instance.Config.General.RootDirectory;

            m_ctmanager = CDare.Instance.Game.Content;
        }

        #endregion

        #region Method

        public static T Load<T>(string resourceName)
            where T : class
        {
            //Debug.Assert(m_currentScene != null);
            Debug.Assert(m_ctmanager != null);
            // cannot load blablabla ? try to rebuild the DAREContentProcessor :P
            T data = Loading<T>(resourceName);
            if (data != null && m_currentScene != null) // && !m_currentScene.Resources.Contains(resourceName))
                m_currentScene.Resources.AddAsset(resourceName);
            if (data != null && typeof(T) == typeof(Effect))
            {
                // effect is null ? add shaders to the content^^
                Effect d = data as Effect;
                d.Name = resourceName;
            }
            return data;
        }

        #region ErrorLoading

        private static T Loading<T>(string resourceName)
            where T : class
        {
            try
            {
                return m_ctmanager.Load<T>(resourceName);
            }
            catch (Exception e)
            {
                if (CDare.Instance.DebugMgr != null)
                    CDare.Instance.DebugMgr.DebugConsole.WriteLine(string.Format("Error Loading : {0}", e.Message));
                if (typeof(T) == typeof(Texture2D))
                {
                    Texture2D tex = new Texture2D(DARE.CDare.Instance.GraphicsDevice, 1, 1);
                    tex.SetData(new Color[]{new Color(255, 0, 255, 255)});
                    return tex as T;
                }
                if (typeof(T) == typeof(Model))
                    return new CBoxModel() as T;
                return null;
            }
        }

        #endregion

        public static T Load<T>(byte[] _fileByte, string resourceName)
            where T : class
        {
            T obj = null;
            using (MemoryStream ms = new MemoryStream(_fileByte))
            {
                obj = Load<T>(ms, resourceName);
            }
            return obj;
        }

        public static T Load<T>(Stream stream, string resourceName)
        {
            //Debug.Assert(m_currentScene != null);
            Debug.Assert(m_ctmanager != null);
            if (m_readAsset == null)
                InitializeMetaInfo();

            resourceName = (string)m_cleanPath.Invoke(null, new Object[] { resourceName });
            if (m_dicoInfo.ContainsKey(resourceName))
                return (T)m_dicoInfo[resourceName];
            MethodInfo readAG = m_readAsset.MakeGenericMethod(typeof(T));
            ContentReader cr = (ContentReader)m_contentReader.Invoke(null, new object[] { m_ctmanager, stream, resourceName, null });
            T data = (T)readAG.Invoke(cr, null);
            m_dicoInfo.Add(resourceName, data);
            if (data != null && m_currentScene != null) // && !m_currentScene.Resources.Contains(resourceName))
                m_currentScene.Resources.AddAsset(resourceName);
            if (typeof(T) == typeof(Effect))
            {
                Effect d = data as Effect;
                d.Name = resourceName;
            }
            return data;
        }

        private static void InitializeMetaInfo()
        {
            Type t = typeof(ContentReader);
            m_contentReader = t.GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic);
            m_readAsset = t.GetMethod("ReadAsset", BindingFlags.NonPublic | BindingFlags.Instance);
            Type cleanT = typeof(TitleContainer);
            m_cleanPath = cleanT.GetMethod("GetCleanPath", BindingFlags.Static | BindingFlags.NonPublic);
            FieldInfo _info = typeof(ContentManager).GetField("loadedAssets", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
            m_dicoInfo = (Dictionary<string, object>)_info.GetValue(m_ctmanager);
        }

        public static void UnLoad(string resourceName)
        {
            if (m_currentScene != null)
            {
                m_currentScene.Resources.RemoveAsset(resourceName);
            }
        }

        /// <summary>
        /// Unload All
        /// </summary>
        public static void UnLoad()
        {
            m_ctmanager.Unload();
        }

        public static void Load(CScene _scene)
        {
            Load(_scene, true);
        }

        public static void Load(CScene _scene, bool unloadLast)
        {
            //if (unloadLast)
            //    m_ctmanager.Unload();
            foreach (string elem in _scene.Resources)
            {
                try
                {
                    m_ctmanager.Load<object>(elem);
                }
                catch (Exception e)
                {
                    //CDebugMessage msg = new CDebugMessage(new CDebugable() { ObjectTypeName = "RessourceManager" },
                    //    "Scene:" + m_currentScene.Name, "Loading asset : " + elem + " -> " + e.Message);
                    //CDebugger.WriteLine(E_SendType.DEBUGTOOLS, E_DebugType.ERROR, msg);
                }
            }
            m_currentScene = _scene;
        }

        /// <summary>
        /// Gets the name of a loaded resource
        /// </summary>
        /// <param name="resource"></param>
        /// <returns></returns>
        public static string GetResourceName(object resource)
        {
            foreach (KeyValuePair<string, object> pair in m_dicoInfo)
                if (pair.Value == resource)
                    return pair.Key;
            return null;
        }

        #endregion
    }
}
